Home > Mac administration, Mac OS X, Scripting > Running processes in OS X as the logged-in user from outside the user’s account

Running processes in OS X as the logged-in user from outside the user’s account

One challenge that can crop up for Mac admins is the problem of running a script or other tool with root privileges and using it to launch and run another tool, script or application as if the logged-in user had launched it. An example of this would be installing Dropbox using an installer package, then launching the Dropbox application as the logged-in user as a post-installation task. One reason to do so would be to give the user the opportunity to sign into their Dropbox account.

To accomplish this task, Apple has provided functionality in the launchctl tool.

For 10.9.x and earlier, launchctl‘s bsexec function was used for this purpose. bsexec allows you to start a process like a tool, script or application in a specific context. One way to get the correct context for the logged-in user is to identify the process identifier (PID) for the loginwindow process associated with the logged-in user, which can be accomplished by using the command below:

ps auxww | grep "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" | grep logged_in_username_here | grep -v "grep" | awk '{print $2}'

For example, if I logged in with an account named username, running the command shown below should provide the PID for the username account’s loginwindow process:

ps auxww | grep "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" | grep username | grep -v "grep" | awk '{print $2}'

Screen Shot 2016 03 25 at 1 08 11 PM

 

Once the PID has been identified, it can be used to identify the context that I want to start a process in. For example, the commands below can be run with root privileges to first identify the correct PID for the username account’s loginwindow process, then launch Safari as the username account using the open command:

ps auxww | grep "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" | grep logged_in_username_here | grep -v "grep" | awk '{print $2}'
launchctl bsexec PID_number_here open "/Applications/Safari.app"

Screen Shot 2016 03 25 at 1 17 19 PM

 

Screen Shot 2016 03 25 at 1 19 46 PM

 

When we check the process list for which account is was launched from, the Safari process should show up as being run by the username account.

Screen Shot 2016 03 25 at 1 18 37 PM

 

If you wanted to automate opening Safari as the logged-in user, you could run a script like the one below with root privileges:


#!/bin/bash
# Identify the username of the logged-in user
logged_in_user=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'`
# Identify the loginwindow PID of the logged-in user
logged_in_user_loginwindow_pid=$(ps auxww | grep "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" | grep "$logged_in_user" | grep -v "grep" | awk '{print $2}')
/bin/launchctl bsexec "$logged_in_user_loginwindow_pid" /usr/bin/open "/Applications/Safari.app"

view raw

gistfile1.txt

hosted with ❤ by GitHub

Starting in OS X Yosemite, Apple made a number of changes to the launchctl tool and added a new asuser function. The asuser function is designed to take the place of the bsexec function, in the context of starting processes in the context of a specific user account. This makes it easier, as you now just need to figure out the username and do not have to figure out the PID of the user’s loginwindow process.

You can identify the user by either the user’s account name, or by the account’s UID. One way to identify an account’s UID is to use the id command as shown below:

id -u username_goes_here

To continue with the example of opening Safari, you can run the commands below to first identify the correct UID, then launch Safari as the username account using the open command:

id -u username_goes_here
launchctl asuser username_uid_goes_here open "/Applications/Safari.app"

 

Screen Shot 2016 03 25 at 2 09 27 PM

Screen Shot 2016 03 25 at 2 07 21 PM

 

Screen Shot 2016 03 25 at 2 49 03 PM

If you wanted to automate opening Safari using the logged-in user’s UID, you could run a script like the one below with root privileges:


#!/bin/bash
# Identify the username of the logged-in user
logged_in_user=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'`
# Identify the UID of the logged-in user
logged_in_user_uid=`id -u "$logged_in_user"`
/bin/launchctl asuser "$logged_in_user_uid" /usr/bin/open "/Applications/Safari.app"

view raw

gistfile1.txt

hosted with ❤ by GitHub

As mentioned earlier, you can also use just the account name with launchctl‘s asuser function. In that case, you can run the command below to launch Safari as the username account using the open command:

launchctl asuser username_goes_here open "/Applications/Safari.app"

If you wanted to automate opening Safari using the logged-in user’s account name, you could run a script like the one below with root privileges:


#!/bin/bash
# Identify the username of the logged-in user
logged_in_user=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'`
/bin/launchctl asuser "$logged_in_user" /usr/bin/open "/Applications/Safari.app"

view raw

gistfile1.txt

hosted with ❤ by GitHub

  1. April 26, 2016 at 4:56 pm

    First of all thank you very much for all your articles and info. I learn a lot from them. Let’s see if this time I can contribute a little bit.

    In your examples you use a python function to get the username of the logged in user inside a script that is run as root (sudo I suppose). As we are shell scripting and we are in bash, I would rather use other methods to get the actual username, mostly because they are shorter to write 😉

    1) The first option would be to use

    echo $USER”

    because even if you use “sudo echo $USER” you get the username of the logged in user and not root. But this option only works inside a script run as sudo, but not a script that does “sudo su” or several concatenated “sudo” instances inside the script.

    So, for simple scripts that are run from the logged in user as sudo it would work.

    2) The second option would be to use “who am i” (yes, with spaces), which works from inside a script run as sudo or inside multiple concatenated sudos or “sudo su” shells. But to get the username only we would need to pass it to awk as

    who am i | awk ‘{print $1}’

    3) But the most elegant option I have found is

    logname

    which works in all instances and we don’t need to pass it to any other function to extract the username.

    I have tried all three in bash and several versions of OS X and they seem to be available always (correct me if I am wrong). What I haven’t tried is to run them in a simple sh script (not bash).

    My two cents

    • Michael Entholzner
      September 16, 2016 at 5:31 am

      Great finding, thank you very much for that!

  2. Chinmoy Saxena
    January 29, 2017 at 1:03 am

    Thank you so very much for all your knowledge that you share. At least for me you have helped me a lot. Thanks again.

  1. No trackbacks yet.

Leave a comment